﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Utils;

namespace Vortex {

	/// <summary>
	/// Encapsulates game time information
	/// </summary>
    public sealed class GameTime {
		/// <summary>Maximal time delta for game time</summary>
        public const float MAX_TIME_DELTA = 1 / 25.0f;

		const float _fpsMeasureTimeFrame = 0.5f;

		private PerfTime _gameStartTime;
		private PerfTime _lastFrameTime;
		private PerfTime _lastFpsTime;

		private PerfTime _pauseStarted;

		private float _totalGameTime;
		private float _frameTime;
		private float _fps;
		private uint  _frameCounter;
		private uint  _fpsFrameCounter;
		private bool  _paused;

        #region Internal Things

		/// <summary>
		/// Initializes a new instance of the <see cref="GameTime"/> class.
		/// </summary>
		internal GameTime() {
	        _lastFrameTime = PerfTime.Now;
	        _gameStartTime = _lastFrameTime;
	        _lastFpsTime = _lastFrameTime;
        }

		/// <summary>
		/// Updates game time with new frame
		/// </summary>
		internal void UpdateFrame() {
	        PerfTime thisFrameSnapshot = PerfTime.Now;
	        PerfTimeSpan timeDelta = thisFrameSnapshot - _lastFrameTime;
	        PerfTimeSpan gameTimeDelta = thisFrameSnapshot - _gameStartTime;
	        PerfTimeSpan fpsTimeDelta = thisFrameSnapshot - _lastFpsTime;

	        _frameCounter++;
	        _fpsFrameCounter++;

	        if (fpsTimeDelta.Seconds >= _fpsMeasureTimeFrame) {
		        _fps = (float)(_fpsFrameCounter) / (float)fpsTimeDelta.Seconds;
		        _fpsFrameCounter = 0;
		        _lastFpsTime = thisFrameSnapshot;
	        }
	        _frameTime = Math.Min((float)timeDelta.Seconds, MAX_TIME_DELTA);
	        _totalGameTime += _frameTime;
	        _lastFrameTime = thisFrameSnapshot;
        }

		///<summary>Begin game time pause to prevent time calculation out of game cycle</summary>
		internal void Pause() {
			if (_paused) {
				_paused = true;
				_pauseStarted = PerfTime.Now;
			}
        }

		///<summary>Resume game time after some wait period</summary>
		internal void Resume() {
			if (_paused) {
				PerfTimeSpan pauseDelta = PerfTime.Now - _pauseStarted;
				_lastFrameTime += pauseDelta;
				_lastFpsTime += pauseDelta;
				_paused = false;
			}
        }

		///<summary>True if game time is currently paused</summary>
		internal bool Paused {
			get { return _paused; }
		}

	    #endregion

		#region Public Properties

		/// <summary>
		/// Gets the length of current frame in seconds
		/// </summary>
		/// <value>The total game time.</value>
		public float TotalGameTime {
			get { return _totalGameTime; }
		}

		/// <summary>
		/// Gets the length of current frame in seconds
		/// </summary>
		/// <value>The time of last frame.</value>
        public float FrameTime {
			get { return _frameTime; }
		}

		/// <summary>
		/// Get the length of current frame in milliseconds
		/// </summary>
		/// <value>The frame time ms.</value>
        public float FrameTimeMs {
			get { return _frameTime * 1000.0f; }
		}

		/// <summary>
		/// Gets the number of frames per second
		/// </summary>
		/// <value>The FPS.</value>
        public float Fps {
			get { return _fps; }
		}

		#endregion
	}
}
